/** @file
Copyright (c) 2006-2023, Kunlun BIOS, KunLun Technology (Beijing) Co., Ltd.. All
Rights Reserved.

You may not reproduce, distribute, publish, display, perform, modify, adapt,
transmit, broadcast, present, recite, release, license or otherwise exploit
any part of this publication in any form, by any means, without the prior
written permission of KunLun Technology (Beijing) Co., Ltd..

Module Name:

  KlDevicePathOrderLib.c

**/

#include <Library/KlCpuAndDdrSettingLib.h>

/**
  Gets the enabled status of the CPU cores from SETUP screen.

  @return CoreBitMap
**/
EFI_STATUS
GetCoreBitMap(
    UINT32 * CoreBitMap
)
{
    UINT32          CoreIndex ;
    UINT32          Map;
    SYSTEM_SETUP_CONFIGURATION   *SetupVar ;
    EFI_STATUS      Status;
    Map = 0;
    SetupVar = NULL;
    if(CoreBitMap == NULL) {
        return EFI_INVALID_PARAMETER;
    }
    Status = KlSetupVarRead(&SetupVar);
    if (EFI_ERROR(Status)) {
        DEBUG ((EFI_D_INFO, "Get CoreBitMap failed! Using Default.\n"));
        return Status;
    }else{
        for (CoreIndex = 0; CoreIndex < sizeof(SetupVar->CoreSettingEnable)/sizeof(SetupVar->CoreSettingEnable[0]); CoreIndex++) {
           if (SetupVar->CoreSettingEnable[CoreIndex] == 1) {
             if ((CoreIndex & 0x1) == 0) {
                Map |= (1<<(CoreIndex<<1));
             }else{
                Map |= (1 << ((CoreIndex<<1)-1));
             }
           }
        }
       *CoreBitMap = 0x00ff&Map;//FT2004 has only four cores
    }
    if(SetupVar != NULL) {
        FreePool(SetupVar);
    }
    return Status;
}


 /**
  Obtain the configuration information for PCIE from SETUP screen.

  @return PeuConfigData
**/
EFI_STATUS
GetPcieSetupData(
    PARAMETER_PEU_CONFIG* PeuConfigData
)
{
    SYSTEM_SETUP_CONFIGURATION* SetupVar;
    UINT8                       Peu0BaseCfg;
    UINT8                       Peu1BaseCfg;
    UINT8                       PeuCoreLinkSpeed[TOTAL_PEU_NUMBERS][TOTAL_PEU_CORE_NUMBERS];
    UINT8                       PeuCoreEqualization[TOTAL_PEU_NUMBERS][TOTAL_PEU_CORE_NUMBERS];
    UINT8                       PeuEnable[TOTAL_PEU_NUMBERS][TOTAL_PEU_CORE_NUMBERS];
    EFI_STATUS                  Status;
    UINT8                       Index;
    SetupVar = NULL;
    ZeroMem(PeuCoreLinkSpeed, sizeof(PeuCoreLinkSpeed));
    ZeroMem(PeuCoreEqualization, sizeof(PeuCoreEqualization));
    ZeroMem(PeuEnable, sizeof(PeuEnable));
    Status = KlSetupVarRead(&SetupVar);
    if (!EFI_ERROR(Status)) {
        Peu0BaseCfg = SetupVar->PcieSettingSplitMode[0] & 0x0f;
        Peu1BaseCfg = SetupVar->PcieSettingSplitMode[1] & 0x0f;
        for (Index = 0; Index < TOTAL_PEU_CORE_NUMBERS; ++Index) {
        PeuEnable[0][Index] = SetupVar->PcieSettingPeu0Enable[Index] & 0x01;
        PeuEnable[1][Index] = SetupVar->PcieSettingPeu1Enable[Index] & 0x01;
        PeuCoreLinkSpeed[0][Index] = SetupVar->PcieSettingPeu0Speed[Index];
        PeuCoreLinkSpeed[1][Index] = SetupVar->PcieSettingPeu1Speed[Index];
        PeuCoreEqualization[0][Index] = SetupVar->PcieSettingPeu0Eq[Index];
        PeuCoreEqualization[1][Index] = SetupVar->PcieSettingPeu1Eq[Index];
        PeuConfigData->Peu0ControllerConfig[Index] = (PeuConfigData->Peu0ControllerConfig[Index] & 0xfffff000) | (PeuEnable[0][Index] << PEU_C_OFFSET_ENABLE) | PeuCoreLinkSpeed[0][Index];
        PeuConfigData->Peu1ControllerConfig[Index] = (PeuConfigData->Peu1ControllerConfig[Index] & 0xfffff000) | (PeuEnable[1][Index] << PEU_C_OFFSET_ENABLE) | PeuCoreLinkSpeed[1][Index];
        PeuConfigData->Peu0ControllerEqualization[Index] = (PeuConfigData->Peu0ControllerEqualization[Index] & 0xfffff000) | PeuCoreEqualization[0][Index];
        PeuConfigData->Peu1ControllerEqualization[Index] = (PeuConfigData->Peu1ControllerEqualization[Index] & 0xfffff000) | PeuCoreEqualization[1][Index];
        }
        PeuConfigData->FunctionConfig &= ~(0x0F | (0x0000F << PEU1_OFFSET));
        PeuConfigData->FunctionConfig |= Peu0BaseCfg | (Peu1BaseCfg << PEU1_OFFSET);
    } else {
        DEBUG((EFI_D_INFO, "Get Pcie Setup data failed! Using Default.\n"));
    }
    if (SetupVar != NULL) {
        FreePool(SetupVar);
    }
    return Status;
}

 /**
 Gets the frequency of the CPU from SETUP screen.

  @return CpuSpeedSetDate
**/
EFI_STATUS
GetCpuSpedSetDate (
    UINT32 * CpuSpeedSetDate
)
{
    SYSTEM_SETUP_CONFIGURATION    *SetupVar;
    EFI_STATUS         Status;
    SetupVar = NULL;
    Status = KlSetupVarRead(&SetupVar);
    if (!EFI_ERROR(Status)&&(SetupVar->CoreSpeed >= MIN_CORE_FREQ)&&(SetupVar->CoreSpeed <= MAX_CORE_FREQ)) {
        *CpuSpeedSetDate = SetupVar->CoreSpeed;
        FreePool(SetupVar);
        return EFI_SUCCESS;
    }
    else{
        DEBUG ((EFI_D_INFO, "Get Core Speed failed! Using Default.\n"));
        if(SetupVar != NULL) {
            FreePool(SetupVar);
        }
    }
    return EFI_INVALID_PARAMETER;
}

 /**
  Gets the frequency of the DDR from SETUP screen.

  @return LmuSpeedSetDate
**/
EFI_STATUS
GetLmuSpeedSetDate (
  UINT32 *LmuSpeedSetDate
)
{
    SYSTEM_SETUP_CONFIGURATION    *SetupVar;
    EFI_STATUS         Status;
    SetupVar = NULL;
    Status = KlSetupVarRead(&SetupVar);
    if (!EFI_ERROR(Status)&&(SetupVar->LmuSpeed>0)) {
        *LmuSpeedSetDate = SetupVar->LmuSpeed;
        FreePool(SetupVar);
        return EFI_SUCCESS;
    }
    else{
        DEBUG ((EFI_D_INFO, "Get Lmu Speed failed! Using Default.\n"));
        if(SetupVar != NULL) {
            FreePool(SetupVar);
        }
    }
    return EFI_INVALID_PARAMETER;
}

 /**
  Complete the configuration of the data by erasing and writing flash.
**/
EFI_STATUS
SetCurrentCofigByBuffer(
  VOID
)
{
    PARAMETER_PEU_CONFIG       *PeuConfigData;
    EFI_STATUS                  Status;
    UINT32                      Index;
    UINT32                      AddrOffeset;
    UINT32                      CpuSpeedSetDate;
    UINT32                      LmuSpeedSetDate;
    UINT32                      CoreBitMap;
    UINTN                       *Address;
    UINT8                       *ConfigurationBuffer;
    ConfigurationBuffer = NULL;
    PeuConfigData = NULL;
    Address = (UINTN*)(PARAMETER_ADDRESS64);
    ConfigurationBuffer = AllocateZeroPool(BE64KSIZE);
    if(ConfigurationBuffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
    }
    CopyMem(ConfigurationBuffer, (VOID *)(Address), BE64KSIZE);

    //
    //Get the set CPU frequency and store it in the corresponding location of BUFFER
    //
    Status = GetCpuSpedSetDate (&CpuSpeedSetDate);
    AddrOffeset = CPU_SPEED_OFFSET;
    if(!Status){
        for(Index=0;Index<(sizeof(UINT32)/sizeof(UINT8));Index++) {
        *((UINT8 *)(ConfigurationBuffer + AddrOffeset + Index)) = \
        (*((UINT8*)(&CpuSpeedSetDate) + Index) & 0xFF);
        }
    }

    //
    //Get the set Lmu frequency and store it in the corresponding location of BUFFER
    //
    Status = GetLmuSpeedSetDate (&LmuSpeedSetDate);
    AddrOffeset = LMU_SPEED_OFFSET;
    if(!Status){
        for(Index=0;Index<(sizeof(UINT32)/sizeof(UINT8));Index++) {
        *((UINT8 *)(ConfigurationBuffer + AddrOffeset + Index)) = \
        (*((UINT8*)&LmuSpeedSetDate + Index) & 0xFF);
        }
    }

    //
    //Get the set CoreBitMap and store it in the corresponding location of BUFFER
    //
    Status = GetCoreBitMap(&CoreBitMap);
    AddrOffeset = CPU_SETTING_OFFSET;
    if(!Status){
        for(Index=0;Index<(sizeof(UINT32)/sizeof(UINT8));Index++) {
        *((UINT8 *)(ConfigurationBuffer + AddrOffeset + Index)) = \
        (*((UINT8*)&CoreBitMap + Index) & 0xFF);
         }
    }

    //
    //Get the PCIe set Date and store it in the corresponding location of BUFFER
    //
    PeuConfigData = AllocatePool(sizeof(PARAMETER_PEU_CONFIG));
    ZeroMem (PeuConfigData, sizeof(PARAMETER_PEU_CONFIG));
    Address = (UINTN*)(PARAMETER_ADDRESS64+PCIE_SETTING_PEU_OFFSET);
    AddrOffeset = PCIE_SETTING_PEU_OFFSET;
    CopyMem(PeuConfigData, (VOID*)(Address), sizeof(PARAMETER_PEU_CONFIG));
    Status = GetPcieSetupData(PeuConfigData);
    if(!Status){
        for(Index=0;Index<(sizeof(PARAMETER_PEU_CONFIG)/sizeof(UINT8));Index++) {
        *((UINT8 *)(ConfigurationBuffer + AddrOffeset + Index)) = \
        (*((UINT8*)PeuConfigData + Index) & 0xFF);
        }
    }

    //
    //Burn the set data to flash
    //
    Address = (UINTN*)(PARAMETER_ADDRESS64);
    Status = FlashErase((UINTN)(Address));
    if(EFI_ERROR(Status)) {
        FreePool(ConfigurationBuffer);
        return Status;
    }

    Status = FlashWrite((UINT8 *)ConfigurationBuffer, (UINT8 *)(UINTN)(Address), BE64KSIZE / 4);
    FreePool(ConfigurationBuffer);
    if(PeuConfigData != NULL){
       FreePool(PeuConfigData);
        }
    return Status;
}

/**
    Get the Cpu Ddr Setting Flag.

   @return BOOLEAN
 **/
BOOLEAN
GetCpuDdrSettingFlag(
  VOID
)
{
    EFI_STATUS                  Status;
    PARAMETER_PEU_CONFIG        *PeuConfigData1;
    PARAMETER_PEU_CONFIG        *PeuConfigData2;
    UINT32                      *SetValueAddress;
    UINT32                      CoreBitMap;
    SYSTEM_SETUP_CONFIGURATION  *SetupVar;
    UINT8                       Index;
    UINT8                       IsNotSetFlag;
    IsNotSetFlag = TRUE;
    SetupVar = NULL;
    PeuConfigData1 = NULL;
    PeuConfigData2 = NULL;
    CoreBitMap = 0;
    Status = KlSetupVarRead(&SetupVar);
    if (Status) {
        FreePool(SetupVar);
        return FALSE;
    }

    SetValueAddress = (UINT32*)(PARAMETER_ADDRESS64 + CPU_SPEED_OFFSET);
    if((SetupVar->CoreSpeed != 0)&&(SetupVar->CoreSpeed != *SetValueAddress)){
        if(IsNotSetFlag){
            IsNotSetFlag = FALSE;
        }
    }

    SetValueAddress = (UINT32*)(PARAMETER_ADDRESS64 + LMU_SPEED_OFFSET);
    if((SetupVar->LmuSpeed != 0)&&(SetupVar->LmuSpeed != *SetValueAddress)){
        if(IsNotSetFlag){
            IsNotSetFlag = FALSE;
        }
    }

    SetValueAddress = (UINT32*)(PARAMETER_ADDRESS64 + CPU_SETTING_OFFSET);
    Status = GetCoreBitMap(&CoreBitMap);
    if(!Status){
      *SetValueAddress = *SetValueAddress&0x00ff;
      if(CoreBitMap != *SetValueAddress){
        if(IsNotSetFlag){
            IsNotSetFlag = FALSE;
        }
      }
    }

    SetValueAddress = (UINT32*)(PARAMETER_ADDRESS64+PCIE_SETTING_PEU_OFFSET);
    PeuConfigData1 = AllocatePool(sizeof(PARAMETER_PEU_CONFIG));
    PeuConfigData2 = AllocatePool(sizeof(PARAMETER_PEU_CONFIG));
    ZeroMem (PeuConfigData1, sizeof(PARAMETER_PEU_CONFIG));
    ZeroMem (PeuConfigData2, sizeof(PARAMETER_PEU_CONFIG));
    CopyMem(PeuConfigData1,(VOID*)SetValueAddress, sizeof(PARAMETER_PEU_CONFIG));
    CopyMem(PeuConfigData2,(VOID*)SetValueAddress, sizeof(PARAMETER_PEU_CONFIG));
    Status = GetPcieSetupData(PeuConfigData1);
    if (!Status) {
        if(PeuConfigData1->FunctionConfig != PeuConfigData2->FunctionConfig){
            if(IsNotSetFlag){
                IsNotSetFlag = FALSE;
            }
        }
        for( Index=0; Index<OTAL_PEU_CORE_NUMBERS; Index++){
            if(PeuConfigData1->Peu0ControllerConfig[Index] != PeuConfigData2->Peu0ControllerConfig[Index]){
                if(IsNotSetFlag){
                    IsNotSetFlag = FALSE;
                }
            }
            if(PeuConfigData1->Peu1ControllerConfig[Index] != PeuConfigData2->Peu1ControllerConfig[Index]){
                if(IsNotSetFlag){
                    IsNotSetFlag = FALSE;
                }
            }
            if(PeuConfigData1->Peu0ControllerEqualization[Index] != PeuConfigData2->Peu1ControllerEqualization[Index]){
                if(IsNotSetFlag){
                    IsNotSetFlag = FALSE;
                }
            }
            if(PeuConfigData1->Peu1ControllerEqualization[Index] != PeuConfigData2->Peu1ControllerEqualization[Index]){
                if(IsNotSetFlag){
                    IsNotSetFlag = FALSE;
                }
            }
        }
    }
    FreePool(PeuConfigData1);
    FreePool(PeuConfigData2);
    FreePool(SetupVar);
    if(IsNotSetFlag){
        return FALSE;
    }

    else{
        return TRUE;
    }
}



/**
    Cpu And Ddr Setting Initialize.
 **/
EFI_STATUS
EFIAPI
KlCpuAndDdrSettingInit(
  VOID
)
{
    EFI_STATUS Status;
    BOOLEAN CpuDdrSettingFlag;
    Status = EFI_SUCCESS;
    CpuDdrSettingFlag = GetCpuDdrSettingFlag();
    if(CpuDdrSettingFlag){
        //Status = CheckParameterConfigSpaceMagic();
        Status = SetCurrentCofigByBuffer();
    }
    return Status;
}
